//Defined for both C++ and HLSL
//

#define SHADOW_EPSILON_SPOT 0.001f
#define SHADOW_EPSILON_DIRECTIONAL 0.001f
#define SHADOW_MAP_HEIGHT_WIDTH 1024
#define SMAP_DX 1.0f / SHADOW_MAP_HEIGHT_WIDTH

#ifndef MAX_LIGHTS
	#define MAX_LIGHTS

	#define max_lights_directional 8
	#define max_lights_point 8
	#define max_lights_spot 8
#endif

#ifdef __cplusplus
	#pragma once

	#include "hhdCommon.h"
	#include "hhdColor.h"

	namespace hhd
	{
		namespace DX11Rendering
		{			
			/**
			* Represents the data for a DirectionalLight
			*/
			struct DIRECTIONAL_LIGHT
			{
				//wvp matrix for shadow map sampling
				Matrix4 _vp;

				Vector4 _direction_ws;//w memory pad

				Color _ambient_color;
				Color _diffuse_color;
				Color _specular_color;

				//stored in x, 1.0 true - 0.0 false, others are memory buffer
				Vector4 _produce_shadow_map;
			};

			/**
			* Represents the data for a PointLight
			*/
			struct POINT_LIGHT
			{
				Vector4 _position_ws;//w memory pad

				Vector4 _attenuation_coefficients;//w memory pad

				Color _ambient_color;
				Color _diffuse_color;
				Color _specular_color;

				Vector4 _fast_falloff;//stored in x,y   z,w memory pad
			};

			/**
			* Represents the data for a SpotLight
			*/
			struct SPOT_LIGHT
			{
				//wvp matrix for shadow map sampling
				Matrix4 _vp;

				Vector4 _position_ws;//w memory pad
				Vector4 _direction_ws_power_pair;

				Vector4 _attenuation_coefficients;//w memory pad

				Color _ambient_color;
				Color _diffuse_color;
				Color _specular_color;				

				Vector4 _fast_falloff;//stored in x,y   z,w memory pad

				//stored in x, 1.0 true - 0.0 false, others are memory buffer
				Vector4 _produce_shadow_map;
			};

			/*
			* This is the second buffer the system has a concept of.
			* It is used for lighting only. We also define the max
			* number of lights an object can be affect by at the top
			* of the file.
			*/
			struct LightingInfo
			{
				Vector4 _specular_exponent;//stored in all

				DIRECTIONAL_LIGHT _directional_lights[max_lights_directional];

				POINT_LIGHT _point_lights[max_lights_point];

				SPOT_LIGHT _spot_lights[max_lights_spot];
			};

		}
	}

#else


	
	/**
	* Represents the data for a DirectionalLight
	*/
	struct DIRECTIONAL_LIGHT
	{
		//wvp matrix for shadow map sampling
		matrix vp;

		float4 direction_ws;//w memory pad

		float4 ambient_color;
		float4 diffuse_color;
		float4 specular_color;

		//stored in x, 1.0 true - 0.0 false, others are memory buffer
		float4 produce_shadow_map;
	};

	/**
	* Represents the data for a PointLight
	*/
	struct POINT_LIGHT
	{
		float4 position_ws;

		float4 attenuation_coefficients;//w memory pad

		float4 ambient_color;
		float4 diffuse_color;
		float4 specular_color;

		float4 fast_falloff;//stored in x,y   z,w memory pad
	};

	/**
	* Represents the data for a SpotLight
	*/
	struct SPOT_LIGHT
	{
		//wvp matrix for shadow map sampling
		matrix vp;

		float4 position_ws;
		float3 direction_ws;
		float power;

		float4 attenuation_coefficients;//w memory pad

		float4 ambient_color;
		float4 diffuse_color;
		float4 specular_color;

		float4 fast_falloff;//stored in x,y   z,w memory pad

		//stored in x, 1.0 true - 0.0 false, others are memory buffer
		float4 produce_shadow_map;
	};

	//this buffer always assumes use of register 1
	cbuffer LightingInfo : register( b1 )
	{
		float4 specular_exponent;//stored in all

		DIRECTIONAL_LIGHT directional_lights[max_lights_directional];

		POINT_LIGHT point_lights[max_lights_point];

		SPOT_LIGHT spot_lights[max_lights_spot];
	}

	/**
	* This function will calculate the amount of light to add from a Directional Light
	* given pixel information.
	*/
	float4 addColorFromDirectionalLight( DIRECTIONAL_LIGHT directional_light, float4 camera_position_ws,
		float4 position_ws, float3 normal_ws, float4 material_ambient,
		float4 material_diffuse, float4 material_specular, float specular_exponent )
	{
		float4 result = float4(0,0,0,1);

		/* ambient light */
		if( any( directional_light.ambient_color ) )
		{
			result += saturate( directional_light.ambient_color * material_ambient);
		}

		/* diffuse light */
		if( any( directional_light.diffuse_color ) )
		{
			//n dot L
			float kD = dot( normal_ws, (float3)(-directional_light.direction_ws) );
				
			//clamp floor to 0
			kD = max( kD, 0 );
				
			result += saturate(kD * (directional_light.diffuse_color * material_diffuse));		
		}

		/* specular light */
		if( any( directional_light.specular_color ) )
		{
			//check L dot n first
			float3 L = (float3)(-directional_light.direction_ws);

			if( dot( normal_ws, L ) > 0 )
			{
				// I first
				float3 r = -L;
				r = normalize( r );

				//now get r
				r = reflect( r, normal_ws );

				//get v
				float3 v = (float3)(camera_position_ws - position_ws);
				v = normalize( v );

				//get kS, specular exponent is stored in each of the 4 spots in specular_exponent
				float kS = pow( max( dot(v,r), 0), specular_exponent );

				// kS * cS * mS
				result += saturate(kS * (directional_light.specular_color * material_specular));
			}
		}

		//return the light to add
		return result;
	}

	/**
	* This function will calculate the amount of light to add from a Directional Light
	* given pixel information. It also takes in shadow factor
	*/
	float4 addColorFromDirectionalLight( DIRECTIONAL_LIGHT directional_light, float4 camera_position_ws,
		float4 position_ws, float3 normal_ws, float4 material_ambient,
		float4 material_diffuse, float4 material_specular, float specular_exponent, float shadow_factor )
	{
		float4 result = float4(0,0,0,1);

		/* ambient light */
		if( any( directional_light.ambient_color ) )
		{
			result += saturate( directional_light.ambient_color * material_ambient);
		}

		/* diffuse light */
		if( any( directional_light.diffuse_color ) )
		{
			//n dot L
			float kD = dot( normal_ws, (float3)(-directional_light.direction_ws) );
				
			//clamp floor to 0
			kD = max( kD, 0 );
				
			result += saturate(shadow_factor * kD * (directional_light.diffuse_color * material_diffuse));		
		}

		/* specular light */
		if( any( directional_light.specular_color ) )
		{
			//check L dot n first
			float3 L = (float3)(-directional_light.direction_ws);

			if( dot( normal_ws, L ) > 0 )
			{
				// I first
				float3 r = -L;
				r = normalize( r );

				//now get r
				r = reflect( r, normal_ws );

				//get v
				float3 v = (float3)(camera_position_ws - position_ws);
				v = normalize( v );

				//get kS, specular exponent is stored in each of the 4 spots in specular_exponent
				float kS = pow( max( dot(v,r), 0), specular_exponent );

				// kS * cS * mS
				result += saturate(shadow_factor * kS * (directional_light.specular_color * material_specular));
			}
		}

		//return the light to add
		return result;
	}


	/**
	* This function will calculate the amount of light to add from a Point Light
	* given pixel information.
	*/

	float4 addColorFromPointLight( POINT_LIGHT point_light, float4 camera_position_ws,
		float4 position_ws, float3 normal_ws, float4 material_ambient,
		float4 material_diffuse, float4 material_specular, float specular_exponent )
	{
		float4 result = float4(0,0,0,1);

		/* ambient light */
		if( any(  point_light.ambient_color ) )
		{
			result += saturate(point_light.ambient_color * material_ambient);
		}

		/* diffuse light */
		if( any( point_light.diffuse_color ) )
		{				
			// L first
			float3 L = (float3)(point_light.position_ws - position_ws);
			L = normalize( L );

			//now n dot L
			float kD = dot( normal_ws, L );

			//clamp floor to 0
			kD = max( kD, 0 );

			// kD * cD * mD
			result += saturate(kD * (point_light.diffuse_color * material_diffuse));
		}

		/* specular light */
		if( any(point_light.specular_color) )
		{
			//check L dot n first, don't bother to normalize L
			float3 L = (float3)(point_light.position_ws - position_ws);

			if( dot( normal_ws, L ) > 0 )
			{
				// I first
				float3 r = -L;
				r = normalize( r );

				//now get r
				r = reflect( r, normal_ws );

				//get v
				float3 v = (float3)(camera_position_ws - position_ws);
				v = normalize( v );

				//get kS, specular exponent is stored in each of the 4 spots in specular_exponent
				float kS = pow( max( dot(v,r), 0), specular_exponent );

				// kS * cS * mS
				result += saturate(kS * (point_light.specular_color * material_specular));
			}

		}
		//calculate distance
		float d = distance( position_ws, point_light.position_ws );
		//add the light to the total light using attenuation.
		if( any(result.xyz) )
		{			
			//first options concave down curve
			result = result*(1.0f- pow(d/point_light.fast_falloff.y, 2.0f));

			//second options concave down curve
			//if( d < point_light.fast_falloff.y)
				//result = result*pow(((d/point_light.fast_falloff.y)-1),2.0f);
			//else
				//result.xyz = 0.0f;
		}
		return saturate(result/*-0.001f*/);
	}

	/**
	* This function will calculate the amount of light to add from a Spot Light
	* given pixel information.
	*/

	float4 addColorFromSpotLight( SPOT_LIGHT spot_light, float4 camera_position_ws,
		float4 position_ws, float3 normal_ws, float4 material_ambient,
		float4 material_diffuse, float4 material_specular, float specular_exponent )
	{
		float4 result = float4(0,0,0,1);

		/* ambient light */
		if( any(  spot_light.ambient_color ) )
		{
			result += saturate(spot_light.ambient_color * material_ambient);
		}

		/* diffuse light */
		if( any( spot_light.diffuse_color ) )
		{				
			// L first
			float3 L = (float3)(spot_light.position_ws - position_ws);
			L = normalize( L );

			//now n dot L
			float kD = dot( normal_ws, L );

			//clamp floor to 0
			kD = max( kD, 0 );

			// kD * cD * mD
			result += saturate(kD * (spot_light.diffuse_color * material_diffuse));
		}

		/* specular light */
		if( any(spot_light.specular_color) )
		{
			//check L dot n first, don't bother to normalize L
			float3 L = (float3)(spot_light.position_ws - position_ws);

			if( dot( normal_ws, L ) > 0 )
			{
				// I first
				float3 r = -L;
				r = normalize( r );

				//now get r
				r = reflect( r, normal_ws );

				//get v
				float3 v = (float3)(camera_position_ws - position_ws);
				v = normalize( v );

				//get kS, specular exponent is stored in each of the 4 spots in specular_exponent
				float kS = pow( max( dot(v,r), 0), specular_exponent );

				// kS * cS * mS
				result += saturate(kS * (spot_light.specular_color * material_specular));
			}

		}
		//calculate distance
		float d = distance( position_ws, spot_light.position_ws );

		//calculate the spot falloff coefficient
		
		float3 negL = (float3)(position_ws - spot_light.position_ws);
		negL = normalize( negL );

		// kSpot = (max(-L dot d, 0))^power
		float kSpot = max(dot(negL, spot_light.direction_ws),0);
		kSpot = pow( kSpot, spot_light.power );


		//add the light to the total light using attenuation.
		if( any(result.xyz) )
		{
			result = kSpot*result*(1.0f- pow(d/spot_light.fast_falloff.y, 2.0f));
			//if( d < spot_light.fast_falloff.y)
				//result = result*pow(((d/spot_light.fast_falloff.y)-1),2.0f);
			//else
				//result.xyz = 0.0f;
		}
		return saturate(result/*-0.001f*/);
	}

	/**
	* This function will calculate the amount of light to add from a Spot Light
	* given pixel information. This also takes in shadow information
	*/

	float4 addColorFromSpotLight( SPOT_LIGHT spot_light, float4 camera_position_ws,
		float4 position_ws, float3 normal_ws, float4 material_ambient,
		float4 material_diffuse, float4 material_specular, float specular_exponent, float shadow_factor )
	{
		float4 result = float4(0,0,0,1);

		/* ambient light */
		if( any(  spot_light.ambient_color ) )
		{
			result += saturate(spot_light.ambient_color * material_ambient);
		}

		/* diffuse light */
		if( any( spot_light.diffuse_color ) )
		{				
			// L first
			float3 L = (float3)(spot_light.position_ws - position_ws);
			L = normalize( L );

			//now n dot L
			float kD = dot( normal_ws, L );

			//clamp floor to 0
			kD = max( kD, 0 );

			// kD * cD * mD
			result += saturate(shadow_factor * kD * (spot_light.diffuse_color * material_diffuse));
		}

		/* specular light */
		if( any(spot_light.specular_color) )
		{
			//check L dot n first, don't bother to normalize L
			float3 L = (float3)(spot_light.position_ws - position_ws);

			if( dot( normal_ws, L ) > 0 )
			{
				// I first
				float3 r = -L;
				r = normalize( r );

				//now get r
				r = reflect( r, normal_ws );

				//get v
				float3 v = (float3)(camera_position_ws - position_ws);
				v = normalize( v );

				//get kS, specular exponent is stored in each of the 4 spots in specular_exponent
				float kS = pow( max( dot(v,r), 0), specular_exponent );

				// kS * cS * mS
				result += saturate(shadow_factor * kS * (spot_light.specular_color * material_specular));
			}

		}
		//calculate distance
		float d = distance( position_ws, spot_light.position_ws );

		//calculate the spot falloff coefficient
		
		float3 negL = (float3)(position_ws - spot_light.position_ws);
		negL = normalize( negL );

		// kSpot = (max(-L dot d, 0))^power
		float kSpot = max(dot(negL, spot_light.direction_ws),0);
		kSpot = pow( kSpot, spot_light.power );


		//add the light to the total light using attenuation.
		if( any(result.xyz) )
		{
			result = kSpot*result*(1.0f- pow(d/spot_light.fast_falloff.y, 2.0f));
		}
		return saturate(result);
	}


	
/*************************
* ShadowMap Sampling functions for readability of shader code
*/
//Spot Light maps
Texture2D sl0_map : register ( t5 );
Texture2D sl1_map : register ( t6 );
Texture2D sl2_map : register ( t7 );
Texture2D sl3_map : register ( t8 );

Texture2D sl4_map : register ( t9 );
Texture2D sl5_map : register ( t10 );
Texture2D sl6_map : register ( t11 );
Texture2D sl7_map : register ( t12 );

//Directional Light maps
Texture2D dl0_map : register ( t13 );
Texture2D dl1_map : register ( t14 );
Texture2D dl2_map : register ( t15 );
Texture2D dl3_map : register ( t16 );

Texture2D dl4_map : register ( t17 );
Texture2D dl5_map : register ( t18 );
Texture2D dl6_map : register ( t19 );
Texture2D dl7_map : register ( t20 );

//samplers
SamplerState clamped_point_sampler : register( s1 );

float shadowFactorSL0(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 0.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = sl0_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = sl0_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = sl0_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = sl0_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_SPOT;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_SPOT;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_SPOT;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_SPOT;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

float shadowFactorSL1(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 0.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = sl1_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = sl1_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = sl1_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = sl1_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_SPOT;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_SPOT;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_SPOT;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_SPOT;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

float shadowFactorSL2(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 0.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = sl2_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = sl2_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = sl2_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = sl2_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_SPOT;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_SPOT;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_SPOT;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_SPOT;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

float shadowFactorSL3(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 0.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = sl3_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = sl3_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = sl3_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = sl3_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_SPOT;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_SPOT;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_SPOT;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_SPOT;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

float shadowFactorSL4(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 0.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = sl4_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = sl4_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = sl4_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = sl4_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_SPOT;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_SPOT;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_SPOT;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_SPOT;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

float shadowFactorSL5(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 0.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = sl5_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = sl5_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = sl5_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = sl5_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_SPOT;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_SPOT;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_SPOT;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_SPOT;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

float shadowFactorSL6(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 0.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = sl6_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = sl6_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = sl6_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = sl6_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_SPOT;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_SPOT;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_SPOT;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_SPOT;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

float shadowFactorSL7(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 0.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = sl7_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = sl7_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = sl7_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = sl7_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_SPOT;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_SPOT;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_SPOT;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_SPOT;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

float shadowFactorDL0(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 1.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = dl0_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = dl0_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = dl0_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = dl0_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_DIRECTIONAL;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

float shadowFactorDL1(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 1.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = dl1_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = dl1_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = dl1_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = dl1_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_DIRECTIONAL;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

float shadowFactorDL2(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 1.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = dl2_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = dl2_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = dl2_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = dl2_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_DIRECTIONAL;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

float shadowFactorDL3(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 1.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = dl3_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = dl3_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = dl3_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = dl3_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_DIRECTIONAL;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

float shadowFactorDL4(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 1.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = dl4_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = dl4_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = dl4_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = dl4_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_DIRECTIONAL;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

float shadowFactorDL5(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 1.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = dl5_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = dl5_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = dl5_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = dl5_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_DIRECTIONAL;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

float shadowFactorDL6(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 1.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = dl6_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = dl6_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = dl6_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = dl6_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_DIRECTIONAL;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

float shadowFactorDL7(float4 proj_texcoord_raw)
{
	//complete projections by doing division by w
	float3 proj_texcoord = proj_texcoord_raw.xyz / proj_texcoord_raw.w;

	//points outside our volume are assumed in shadow
	if( proj_texcoord.x < -1.0f || proj_texcoord.x > 1.0f ||
		proj_texcoord.y < -1.0f || proj_texcoord.y > 1.0f ||
		proj_texcoord.z < 0.0f )
		return 1.0f;

	//transform from NDC space to texture space
	float2 uv = float2( 0.5f*proj_texcoord.x + 0.5f, -0.5f*proj_texcoord.y + 0.5f );
	
	//depth in NDC space
	float depth = proj_texcoord.z;

	//do 4 samplings of the shadow map to get depth
	float sample0 = dl7_map.Sample(clamped_point_sampler, uv.xy);
	float sample1 = dl7_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,0));
	float sample2 = dl7_map.Sample(clamped_point_sampler, uv.xy + float2(0,SMAP_DX));
	float sample3 = dl7_map.Sample(clamped_point_sampler, uv.xy + float2(SMAP_DX,SMAP_DX));

	//is the pixel depth <= shadow map value?
	float comparison0 = depth <= sample0 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison1 = depth <= sample1 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison2 = depth <= sample2 + SHADOW_EPSILON_DIRECTIONAL;
	float comparison3 = depth <= sample3 + SHADOW_EPSILON_DIRECTIONAL;

	//transform to texel space
	float2 texel_position = SHADOW_MAP_HEIGHT_WIDTH * uv.xy;

	//figure out the parameter for interpolation
	float2 t = frac( texel_position );

	//interpolate the results
	return lerp( lerp(comparison0, comparison1, t.x), 
				 lerp(comparison2, comparison3, t.x), t.y );

				
}

#endif

